package com.sunyifeng.ch06;

import java.util.*;

import static com.sunyifeng.ch06.Dish.dishTags;
import static com.sunyifeng.ch06.Dish.menu;
import static java.util.stream.Collectors.*;

/**
 * 6.1.1 Collectors的groupingBy()方法
 */
public class Grouping {

    enum CaloricLevel {DIET, NORMAL, FAT}; //日常饮食、正常、肥胖

    public static void main(String ... args) {
        System.out.println("根据菜的种类（肉、鱼、其他）分组: " + groupDishesByType());
        System.out.println("根据菜的种类（肉、鱼、其他）和名称分组: " + groupDishNamesByType());
        //System.out.println("Dish tags grouped by type: " + groupDishTagsByType());
        System.out.println("根据菜的种类（肉、鱼、其他）分组，不要热量大于500卡的: " + groupCaloricDishesByType());
        System.out.println("根据菜的种类（肉、鱼、其他）分组，再根据热量高低分类1: " + groupDishesByCaloricLevel());
        System.out.println("根据菜的种类（肉、鱼、其他）分组，再根据热量高低分类1:: " + groupDishedByTypeAndCaloricLevel());
        System.out.println("根据菜的种类（肉、鱼、其他）分组，并且统计数量: " + countDishesInGroups());
        System.out.println("根据菜的种类（肉、鱼、其他）分组，并且统计哪个热量大: " + mostCaloricDishesByType());
        System.out.println("按类型划分的最多热量的菜肴，不含香料: " + mostCaloricDishesByTypeWithoutOprionals());
        System.out.println("根据菜的种类（肉、鱼、其他）分组，并且统计总热量: " + sumCaloriesByType());
        System.out.println("根据菜的种类（肉、鱼、其他）分组，并且打标签: " + caloricLevelsByType());
    }

    // 根据菜的种类（肉、鱼、其他）分组
    private static Map<Dish.Type, List<Dish>> groupDishesByType() {
        return menu.stream().collect(groupingBy(Dish::getType));
    }

    // 根据菜的种类（肉、鱼、其他）和名称分组
    private static Map<Dish.Type, List<String>> groupDishNamesByType() {
        return menu.stream().collect(groupingBy(Dish::getType, mapping(Dish::getName, toList())));
    }

    private static Map<Dish.Type, Set<String>> groupDishTagsByType() {
        return menu.stream().collect(groupingBy(Dish::getType, flatMapping(dish -> dishTags.get( dish.getName() ).stream(), toSet())));
    }

    // 根据菜的种类（肉、鱼、其他）分组，不要热量大于500卡的
    private static Map<Dish.Type, List<Dish>> groupCaloricDishesByType() {
    //  return menu.stream().filter(dish -> dish.getCalories() > 500).collect(groupingBy(Dish::getType));
        return menu.stream().collect(groupingBy(Dish::getType, filtering(dish -> dish.getCalories() > 500, toList())));
    }

    // 根据菜的种类（肉、鱼、其他）分组，再根据热量高低分类1
    private static Map<CaloricLevel, List<Dish>> groupDishesByCaloricLevel() {
        return menu.stream().collect(
                groupingBy(dish -> {
                    if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                    else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                    else return CaloricLevel.FAT;
                } ));
    }

    // 根据菜的种类（肉、鱼、其他）分组，再根据热量高低分类2
    private static Map<Dish.Type, Map<CaloricLevel, List<Dish>>> groupDishedByTypeAndCaloricLevel() {
        return menu.stream().collect(
                groupingBy(Dish::getType,
                        groupingBy((Dish dish) -> {
                            if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                            else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                            else return CaloricLevel.FAT;
                        } )
                )
        );
    }

    // 根据菜的种类（肉、鱼、其他）分组，并且统计数量
    private static Map<Dish.Type, Long> countDishesInGroups() {
        return menu.stream().collect(groupingBy(Dish::getType, counting()));
    }

    // 根据菜的种类（肉、鱼、其他）分组，并且统计哪个热量大
    private static Map<Dish.Type, Optional<Dish>> mostCaloricDishesByType() {
        return menu.stream().collect(
                groupingBy(Dish::getType,
                        reducing((Dish d1, Dish d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2)));
    }

    // 按类型划分的最多热量的菜肴，不含香料
    private static Map<Dish.Type, Dish> mostCaloricDishesByTypeWithoutOprionals() {
        return menu.stream().collect(
                groupingBy(Dish::getType,
                        collectingAndThen(
                                reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2),
                                Optional::get)));
    }

    // 根据菜的种类（肉、鱼、其他）分组，并且统计总热量
    private static Map<Dish.Type, Integer> sumCaloriesByType() {
        return menu.stream().collect(groupingBy(Dish::getType,
                summingInt(Dish::getCalories)));
    }

    // 根据菜的种类（肉、鱼、其他）分组，并且打标签
    private static Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType() {
        return menu.stream().collect(
                groupingBy(Dish::getType, mapping(
                        dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                        else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                        else return CaloricLevel.FAT; },
                        toSet() )));
    }
}
